package com.nlx.notes.module.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nlx.notes.module.bean.dto.accountbook.AccountBookDeleteDTO;
import com.nlx.notes.module.bean.dto.accountbook.AccountBookSaveDTO;
import com.nlx.notes.module.bean.dto.accountbook.AccountBookUpdateDTO;
import com.nlx.notes.module.bean.vo.accountbook.AccountBookVO;
import com.nlx.notes.module.core.consts.RedisConst;
import com.nlx.notes.module.core.enums.error.AccountBookCodeEnum;
import com.nlx.notes.module.core.util.DataConvertorUtils;
import com.nlx.notes.module.core.util.DateUtils;
import com.nlx.notes.module.core.util.UUIDUtils;
import com.nlx.notes.module.entity.AccountBooks;
import com.nlx.notes.module.entity.UserCurrentBook;
import com.nlx.notes.module.entity.Users;
import com.nlx.notes.module.mapper.AccountBooksMapper;
import com.nlx.notes.module.mapper.UserCurrentBookMapper;
import com.nlx.notes.module.service.IAccountBooksService;
import com.nlx.notes.support.handler.exception.BusinessException;
import com.nlx.notes.support.helper.redis.RedisHelper;
import com.nlx.notes.support.util.UniqueCodeGenerator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 账本表 服务实现类
 * </p>
 *
 * @author nlx
 * @since 2025-11-28
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class AccountBooksServiceImpl extends ServiceImpl<AccountBooksMapper, AccountBooks> implements IAccountBooksService {

    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    final RedisHelper redisHelper;
    final UserCurrentBookMapper userCurrentBookMapper;

    @Override
    public List<AccountBookVO> listByUser(Long userId) {
        if (userId == null) {
            return Collections.emptyList();
        }
        AccountBookVO currentBook = this.getCurrentBook(userId);

        List<AccountBooks> accountBooks = this.getBaseMapper().selectList(
                Wrappers.lambdaQuery(AccountBooks.class)
                        .eq(AccountBooks::getUserId, userId)
                        .eq(AccountBooks::getDelFlag, 0)
                        .orderByDesc(AccountBooks::getCreatedTime)
        );
        if (CollUtil.isEmpty(accountBooks)) {
            return Collections.emptyList();
        }
        return DataConvertorUtils.copyListProperties(accountBooks, AccountBookVO::new, (source, target) -> {
            target.setIsUse(currentBook.getId().equals(source.getId()));
            target.setName(source.getBookName());
            target.setDescription(source.getBookDesc());
            target.setCreatedTime(DateUtils.formatDateTime(source.getCreatedTime()));
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AccountBookVO createBook(Long userId, AccountBookSaveDTO saveDTO) {
        if (userId == null) {
            throw new IllegalArgumentException("用户ID不能为空");
        }
        if (StrUtil.isEmpty(saveDTO.getName())) {
            throw new IllegalArgumentException("账本名称不能为空");
        }

        AccountBooks accountBooks = new AccountBooks();
        accountBooks.setBookCode(UniqueCodeGenerator.genCode());
        accountBooks.setBookName(saveDTO.getName());
        accountBooks.setBookDesc(saveDTO.getDescription());
        accountBooks.setCurrency(StrUtil.isEmpty(saveDTO.getCurrency()) ? "CNY" : saveDTO.getCurrency());
        accountBooks.setUserId(userId);
        accountBooks.setCreatedTime(LocalDateTime.now());
        accountBooks.setUpdatedTime(LocalDateTime.now());
        accountBooks.setDelFlag(0);

        this.save(accountBooks);
        log.info("用户 {} 创建账本成功，账本ID: {}, 账本名称: {}", userId, accountBooks.getId(), accountBooks.getBookName());

        return convertToVO(accountBooks);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AccountBookVO updateBook(Long userId, AccountBookUpdateDTO updateDTO) {
        if (userId == null) {
            throw new IllegalArgumentException("用户ID不能为空");
        }
        if (updateDTO.getId() == null) {
            throw new IllegalArgumentException("账本ID不能为空");
        }
        if (StrUtil.isEmpty(updateDTO.getName())) {
            throw new IllegalArgumentException("账本名称不能为空");
        }

        AccountBooks accountBooks = this.getById(updateDTO.getId());
        if (accountBooks == null || accountBooks.getDelFlag() == 1) {
            throw new IllegalArgumentException("账本不存在");
        }
        if (!accountBooks.getUserId().equals(userId)) {
            throw new IllegalArgumentException("无权操作此账本");
        }

        accountBooks.setBookName(updateDTO.getName());
        accountBooks.setBookDesc(updateDTO.getDescription());
        if (StrUtil.isNotEmpty(updateDTO.getCurrency())) {
            accountBooks.setCurrency(updateDTO.getCurrency());
        }
        accountBooks.setUpdatedTime(LocalDateTime.now());

        this.updateById(accountBooks);
        log.info("用户 {} 更新账本成功，账本ID: {}, 账本名称: {}", userId, accountBooks.getId(), accountBooks.getBookName());

        return convertToVO(accountBooks);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteBook(Long userId, AccountBookDeleteDTO deleteDTO) {
        if (userId == null) {
            throw new IllegalArgumentException("用户ID不能为空");
        }
        if (deleteDTO.getId() == null) {
            throw new IllegalArgumentException("账本ID不能为空");
        }

        AccountBooks accountBooks = this.getById(deleteDTO.getId());
        if (accountBooks == null || accountBooks.getDelFlag() == 1) {
            throw new IllegalArgumentException("账本不存在");
        }
        if (!accountBooks.getUserId().equals(userId)) {
            throw new IllegalArgumentException("无权操作此账本");
        }

        // 检查是否是最后一个账本
        long count = this.count(
                Wrappers.lambdaQuery(AccountBooks.class)
                        .eq(AccountBooks::getUserId, userId)
                        .eq(AccountBooks::getDelFlag, 0)
        );
        if (count <= 1) {
            throw new IllegalArgumentException("至少保留一个账本");
        }

        // 软删除
        accountBooks.setDelFlag(1);
        accountBooks.setUpdatedTime(LocalDateTime.now());
        this.updateById(accountBooks);

        log.info("用户 {} 删除账本成功，账本ID: {}, 账本名称: {}", userId, accountBooks.getId(), accountBooks.getBookName());
        
        // 如果删除的是当前账本，清除Redis缓存并更新数据库记录
        AccountBookVO currentBook = getCurrentBook(userId);
        if (currentBook != null && currentBook.getId().equals(deleteDTO.getId())) {
            String redisKey = RedisConst.AccountBook.USER_CURRENT_BOOK_KEY + userId;
            redisHelper.removeKey(redisKey);
            
            // 同时更新数据库中的记录
            UserCurrentBook userCurrentBook = userCurrentBookMapper.selectByUserId(userId);
            if (userCurrentBook != null) {
                // 获取用户的其他账本作为新的当前账本
                List<AccountBooks> otherBooks = this.getBaseMapper().selectList(
                        Wrappers.lambdaQuery(AccountBooks.class)
                                .eq(AccountBooks::getUserId, userId)
                                .eq(AccountBooks::getDelFlag, 0)
                                .ne(AccountBooks::getId, deleteDTO.getId())
                                .orderByDesc(AccountBooks::getCreatedTime)
                                .last("LIMIT 1")
                );
                
                if (CollUtil.isNotEmpty(otherBooks)) {
                    userCurrentBookMapper.updateCurrentBook(userId, otherBooks.get(0).getId());
                } else {
                    // 如果没有其他账本了，删除当前账本记录
                    userCurrentBookMapper.deleteById(userCurrentBook.getId());
                }
            }
            
            log.info("用户 {} 删除的是当前账本，已清除缓存并更新数据库记录", userId);
        }
        
        return true;
    }

    @Override
    public AccountBookVO switchBook(Long userId, Long bookId) {
        if (userId == null) {
            throw new IllegalArgumentException("用户ID不能为空");
        }
        if (bookId == null) {
            throw new IllegalArgumentException("账本ID不能为空");
        }

        AccountBooks accountBooks = this.getById(bookId);
        if (accountBooks == null || accountBooks.getDelFlag() == 1) {
            throw new IllegalArgumentException("账本不存在");
        }
        if (!accountBooks.getUserId().equals(userId)) {
            throw new IllegalArgumentException("无权操作此账本");
        }

        // 将当前账本ID存入Redis
        String redisKey = RedisConst.AccountBook.USER_CURRENT_BOOK_KEY + userId;
        redisHelper.setValue(redisKey, bookId, 
                RedisConst.AccountBook.USER_CURRENT_BOOK_TIMEOUT,
                RedisConst.AccountBook.USER_CURRENT_BOOK_TIMEOUT_UNIT);
        
        // 同时更新数据库中的记录
        userCurrentBookMapper.updateCurrentBook(userId, bookId);

        log.info("用户 {} 切换到账本：{}", userId, accountBooks.getBookName());
        return convertToVO(accountBooks);
    }

    @Override
    public AccountBookVO getCurrentBook(Long userId) {
        if (userId == null) {
            return null;
        }
        // 先从Redis中获取当前账本ID
        String redisKey = RedisConst.AccountBook.USER_CURRENT_BOOK_KEY + userId;
        Long currentBookId = redisHelper.getKey(redisKey, Long.class);

        AccountBooks accountBooks = null;
        if (currentBookId != null) {
            accountBooks = this.getById(currentBookId);
            // 如果账本已被删除，清除缓存
            if (accountBooks == null || accountBooks.getDelFlag() == 1) {
                redisHelper.removeKey(redisKey);
                accountBooks = null;
            }
        }

        // 如果Redis中没有或已失效，从数据库中获取
        if (accountBooks == null) {
            UserCurrentBook userCurrentBook = userCurrentBookMapper.selectByUserId(userId);
            if (userCurrentBook != null) {
                accountBooks = this.getById(userCurrentBook.getBookId());
                // 如果账本已被删除，清除数据库记录
                if (accountBooks == null || accountBooks.getDelFlag() == 1) {
                    userCurrentBookMapper.deleteById(userCurrentBook.getId());
                    accountBooks = null;
                } else {
                    // 将数据库中的记录同步到Redis
                    redisHelper.setValue(redisKey, accountBooks.getId(),
                            RedisConst.AccountBook.USER_CURRENT_BOOK_TIMEOUT,
                            RedisConst.AccountBook.USER_CURRENT_BOOK_TIMEOUT_UNIT);
                    log.info("用户 {} 从数据库获取当前账本：{}", userId, accountBooks.getBookName());
                }
            }
        }

        // 如果数据库中也没有，检查用户是否有任何账本
        if (accountBooks == null) {
            // 查询用户的所有账本
            List<AccountBooks> accountBooksList = this.getBaseMapper().selectByUserIdOrderByCreatedTimeDesc(userId);
            
            if (CollUtil.isEmpty(accountBooksList)) {
                // 用户没有任何账本，创建一个默认账本
                AccountBookSaveDTO saveDTO = new AccountBookSaveDTO();
                saveDTO.setName("默认账本");
                saveDTO.setDescription("系统自动创建的默认账本");
                saveDTO.setCurrency("CNY");
                AccountBookVO defaultBook = this.createBook(userId, saveDTO);
                
                // 设置为当前账本
                redisHelper.setValue(redisKey, defaultBook.getId(),
                        RedisConst.AccountBook.USER_CURRENT_BOOK_TIMEOUT,
                        RedisConst.AccountBook.USER_CURRENT_BOOK_TIMEOUT_UNIT);
                
                // 同时更新数据库中的记录
                userCurrentBookMapper.updateCurrentBook(userId, defaultBook.getId());
                
                log.info("用户 {} 无账本，已创建并设置默认账本：{}", userId, defaultBook.getName());
                return defaultBook;
            } else {
                // 用户有账本但没有关联当前账本，选择最新创建的账本
                accountBooks = accountBooksList.get(0);
                // 自动设置为当前账本
                redisHelper.setValue(redisKey, accountBooks.getId(),
                        RedisConst.AccountBook.USER_CURRENT_BOOK_TIMEOUT,
                        RedisConst.AccountBook.USER_CURRENT_BOOK_TIMEOUT_UNIT);
                
                // 同时更新数据库中的记录
                userCurrentBookMapper.updateCurrentBook(userId, accountBooks.getId());
                
                log.info("用户 {} 有关联账本，自动使用最新账本：{}", userId, accountBooks.getBookName());
            }
        }

        return accountBooks != null ? convertToVO(accountBooks) : null;
    }

    @Override
    public void createDefaultAccountBook(Users user) {
        try {
            AccountBookSaveDTO saveDTO = new AccountBookSaveDTO();
            saveDTO.setName("默认账本");
            saveDTO.setDescription("系统自动创建的默认账本");
            saveDTO.setCurrency("CNY");
            // 创建默认账本
            this.createBook(user.getId(), saveDTO);
            log.info("为用户 {} 创建默认账本成功", user.getId());
        } catch (Exception e) {
            log.error("为用户 {} 创建默认账本失败", user.getId(), e);
            throw new BusinessException(AccountBookCodeEnum.ACCOUNT_BOOK_ADD_ERROR);
        }
    }

    /**
     * 转换为VO
     */
    private AccountBookVO convertToVO(AccountBooks accountBooks) {
        AccountBookVO vo = new AccountBookVO();
        vo.setId(accountBooks.getId());
        vo.setBookCode(accountBooks.getBookCode());
        vo.setName(accountBooks.getBookName());
        vo.setDescription(accountBooks.getBookDesc());
        vo.setCurrency(accountBooks.getCurrency());
        if (accountBooks.getCreatedTime() != null) {
            vo.setCreatedTime(accountBooks.getCreatedTime().format(FORMATTER));
        }
        return vo;
    }
}